﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace TwoPageView3
{
    class FolderTreeViewControl
    {
        private TreeView mTreeView;
        private IFolderTreeViewEvent mListener;
        private UserPreference mUserPreference;
        private string DUMMY_ITEM_NAME = "* please wait *";
        private TpvDirectoryInfo mSelectedDirectoryInfo;

        public FolderTreeViewControl(TreeView tv, IFolderTreeViewEvent listener, UserPreference userPreference) {
            mTreeView = tv;
            mListener = listener;
            mUserPreference = userPreference;
            TreeViewUpdateDriveNodes();
        }

        /// <summary>
        /// select folder programatically
        /// </summary>
        public void SelectFolder(string folderPath) {
            mSelectedDirectoryInfo = FolderPathToDirectoryInfo(folderPath);

            TreeViewFolderTreeUpdate(mSelectedDirectoryInfo, true, true);
            mListener.OnFolderTreeViewSelected(mSelectedDirectoryInfo);
        }

        public TpvDirectoryInfo SelectedDirectoryInfo() {
            return mSelectedDirectoryInfo;
        }

        public void Update() {
            TreeViewFolderTreeUpdate(mSelectedDirectoryInfo, true, true);
        }

        enum TreeViewItemType
        {
            Drive,
            NetworkDrive,
            Folder,
            Pdf,
        }

        private void TreeViewUpdateDriveNodes() {
            try {
                var existDrives = new List<TreeViewItem>();
                string[] drives = System.IO.Directory.GetLogicalDrives();
                foreach (string drive in drives) {
                    var itemSearch = from TreeViewItem item in mTreeView.Items
                                where String.Compare(item.Tag as string, drive) == 0
                                select item;
                    var itemEnumerator = itemSearch.GetEnumerator();

                    if (!itemEnumerator.MoveNext()) {
                        // Tree does not contain this drive. Add.
                        if (drive.Length < 2) {
                            // root directory on UNIX... IGNORE
                            continue;
                        }
                        var item = CreateTreeViewItem(drive, TreeViewItemType.Drive);
                        item.Items.Add(CreateDummyTreeViewItem());
                        mTreeView.Items.Add(item);
                        existDrives.Add(item);
                    } else {
                        UpdateTreeViewItem(itemEnumerator.Current);
                        existDrives.Add(itemEnumerator.Current);
                    }
                }

                var disappearedDrives = new List<TreeViewItem>();
                foreach (var item in mTreeView.Items) {
                    if (!existDrives.Contains(item)) {
                        disappearedDrives.Add(item as TreeViewItem);
                    }
                }
                foreach (var item in disappearedDrives) {
                    mTreeView.Items.Remove(item);
                }
            } catch (System.IO.IOException) {
            }
        }

        private void TreeViewFolderTreeUpdate(TpvDirectoryInfo folderDirInfo, bool bExpand, bool bItemAddDelete) {
            TreeViewUpdateDriveNodes();
            string folderPath = folderDirInfo.FullName;
            string driveLetter = folderPath.Substring(0, 3);
            if (Char.IsLetter(driveLetter[0]) && ':' == driveLetter[1] && '\\' == driveLetter[2]) {
                // drive letter

                foreach (var n in mTreeView.Items) {
                    var item = n as TreeViewItem;
                    if (0 == string.Compare(driveLetter, item.Tag as string, StringComparison.Ordinal)) {
                        string subFolderPath = folderPath.Substring(3);
                        if (3 < folderPath.Length && '\\' == folderPath[3]) {
                            subFolderPath = subFolderPath.Substring(1);
                        }
                        TreeViewNodeSubTreeUpdate_r(item, subFolderPath, bExpand, bItemAddDelete);
                        return;
                    }
                }
                return;
            }

            if ('\\' == driveLetter[0] && '\\' == driveLetter[1]) {
                // UNC path

                string driveName = folderPath;
                int delimiterPos = driveName.IndexOf('\\', 2);
                if (delimiterPos < 0) {
                    return;
                }
                int delimiter2Pos = driveName.IndexOf('\\', delimiterPos + 1);
                if (0 <= delimiter2Pos) {
                    driveName = driveName.Substring(0, delimiter2Pos);
                }
                foreach (var n in mTreeView.Items) {
                    var item = n as TreeViewItem;
                    if (0 == string.Compare(driveName, item.Tag as string, System.StringComparison.Ordinal)) {
                        TreeViewNodeSubTreeUpdate_r(item, folderPath.Substring(delimiter2Pos + 1), bExpand, bItemAddDelete);
                        return;
                    }
                }
                if (bItemAddDelete) {
                    var item = CreateTreeViewItem(driveName, TreeViewItemType.NetworkDrive);
                    mTreeView.Items.Add(item);
                    if (delimiter2Pos < 0) {
                        TreeViewNodeSubTreesUpdate(item, true);
                    } else {
                        string subPathName = folderPath.Substring(delimiter2Pos + 1);
                        TreeViewNodeSubTreeUpdate_r(item, subPathName, bExpand, true);
                    }
                }
            }
        }

        private void TreeViewNodeSubTreesUpdate(TreeViewItem selectedNode, bool bItemAddDelete) {
            try {
                UpdateTreeViewItem(selectedNode);
                if (!bItemAddDelete) {
                    return;
                }
                var selectedDir = FolderPathToDirectoryInfo(FullPath(selectedNode));
                selectedDir.Refresh();
                if (!selectedDir.Exists) {
                    selectedNode.Items.Clear();
                    return;
                }
                var subDirInfo = selectedDir.GetDirectories();
                if (null == subDirInfo) {
                    selectedNode.Items.Clear();
                    return;
                }
                var mapSortedByName = new SortedList<string, TpvDirectoryInfo>(new FilenameComparer());
                foreach (var di in subDirInfo) {
                    // zip folder and folder can have the same foldername so we must do work deliberately
                    if (!mapSortedByName.ContainsKey(di.Name)) {
                        mapSortedByName.Add(di.Name, di);
                    }
                }

                {
                    // first remove unexisted nodes
                    var removeNodeList = new List<TreeViewItem>();
                    foreach (var n in selectedNode.Items) {
                        var item = n as TreeViewItem;
                        if (!mapSortedByName.ContainsKey(item.Tag as string)) {
                            removeNodeList.Add(item);
                        }
                    }
                    foreach (var n in removeNodeList) {
                        selectedNode.Items.Remove(n);
                    }
                    removeNodeList.Clear();
                }

                // di list sorted by display order
                var diSortedByDispOrder = FolderListSortedByDisplayOrder(subDirInfo);

                string path = FullPath(selectedNode);

                // add new directories
                foreach (var di in diSortedByDispOrder) {
                    bool bFound = false;
                    foreach (var n in selectedNode.Items) {
                        var item = n as TreeViewItem;
                        if (di.Name.Equals(item.Tag as string)) {
                            bFound = true;
                            break;
                        }
                    }
                    if (bFound) {
                        continue;
                    }

                    var newItem = CreateTreeViewItem(di.Name, di.IsPdfDir ? TreeViewItemType.Pdf : TreeViewItemType.Folder);
                    selectedNode.Items.Add(newItem);
                    try {
                        if (di.IsSubDirExists()) {
                            newItem.Items.Add(CreateDummyTreeViewItem());
                        }
                    } catch (System.UnauthorizedAccessException) {
                    }
                }
            } catch (System.IO.IOException) {
            } catch (System.UnauthorizedAccessException) {
            }
        }

        private void TreeViewNodeSubTreeUpdate_r(TreeViewItem selectedNode, string path, bool bExpand, bool bItemAddDelete) {
            TreeViewNodeSubTreesUpdate(selectedNode, bItemAddDelete);
            if (bExpand) {
                selectedNode.IsExpanded = true;
            }
            if (0 == path.Length) {
                return;
            }
            int delimiterPos = path.IndexOf(System.IO.Path.DirectorySeparatorChar);
            if (delimiterPos < 0) {
                foreach (var n in selectedNode.Items) {
                    var childItem = n as TreeViewItem;
                    if (path.Equals(childItem.Tag as string)) {
                        TreeViewNodeSubTreesUpdate(childItem, bItemAddDelete);
                        break;
                    }
                }
                if (bItemAddDelete) {
                    foreach (var n in selectedNode.Items) {
                        var childItem = n as TreeViewItem;
                        if (0 == string.Compare(childItem.Tag as string, path, System.StringComparison.Ordinal)) {
                            //mTreeView.SelectedItem = n;
                            return;
                        }
                    }
                }
                return;
            }
            string folderName = path.Substring(0, delimiterPos);
            string subPathName = path.Substring(delimiterPos + 1);
            if (0 == subPathName.Length) {
                Console.WriteLine("D: TreeViewNodeSubTreeUpdate_r({0}, {1}) subPathName.Length == 0", FullPath(selectedNode), path);
                return;
            }
            foreach (var n in selectedNode.Items) {
                var childItem = n as TreeViewItem;
                if (folderName.Equals(childItem.Tag as string)) {
                    TreeViewNodeSubTreeUpdate_r(childItem, subPathName, bExpand, bItemAddDelete);
                    return;
                }
            }
        }

        private System.Collections.Generic.List<TpvDirectoryInfo> FolderListSortedByDisplayOrder(TpvDirectoryInfo[] diArray) {
            var rv = new List<TpvDirectoryInfo>(diArray);
            rv.Sort(CompareNameAsc);
            return rv;
        }

        private int CompareNameAsc(TpvDirectoryInfo l, TpvDirectoryInfo r) {
            System.Diagnostics.Debug.Assert(l != null);
            System.Diagnostics.Debug.Assert(r != null);
            return string.Compare(l.Name, r.Name, System.StringComparison.OrdinalIgnoreCase);
        }

        private class FilenameComparer : System.Collections.Generic.IComparer<string>
        {
            public int Compare(string x, string y) {
                return System.Globalization.CultureInfo.InvariantCulture.CompareInfo.Compare(x, y, System.Globalization.CompareOptions.IgnoreCase);
            }
        }

        private string FullPath(TreeViewItem item) {
            string path = FullPath_r(item);
            if (null == path) {
                throw new System.ArgumentNullException("path");
            }
            return path.Replace(":\\\\", ":\\");
        }

        private string FullPath_r(TreeViewItem item) {
            if (null != item.Parent && null != item.Parent as TreeViewItem) {
                return new System.Text.StringBuilder()
                        .Append(FullPath_r(item.Parent as TreeViewItem))
                        .Append(System.IO.Path.DirectorySeparatorChar)
                        .Append(item.Tag as string).ToString();
            } else {
                return item.Tag as string;
            }
        }

        private TpvDirectoryInfo FolderPathToDirectoryInfo(string path) {
            return TpvDirectoryInfo.Factory(path);
        }

        private TreeViewItem CreateDummyTreeViewItem() {
            return new TreeViewItem() { Header = DUMMY_ITEM_NAME, Tag = DUMMY_ITEM_NAME };
        }

        private TreeViewItem CreateTreeViewItem(string name, TreeViewItemType type) {
            var item = new TreeViewItem() { Header = name, Tag=name };

            item.Selected += OnTreeViewItemSelected;
            item.Expanded += OnTreeViewItemExpanded;

            //node.ImageIndex = (int)ImageListImageId.Drive;
            //node.SelectedImageIndex = (int)ImageListImageId.Drive;

            //node.ImageIndex = (int)ImageListImageId.NetDrive;
            //node.SelectedImageIndex = (int)ImageListImageId.NetDrive;

            /*
            if (di.IsZipDir) {
                node.ImageIndex = (int)ImageListImageId.ZipFolderClosed;
                node.SelectedImageIndex = (int)ImageListImageId.ZipFolderOpen;
            } else if (di.IsPdfDir) {
                node.ImageIndex = (int)ImageListImageId.PdfFolderClosed;
                node.SelectedImageIndex = (int)ImageListImageId.PdfFolderOpen;
            } else {
                node.ImageIndex = (int)ImageListImageId.FolderClosed;
                node.SelectedImageIndex = (int)ImageListImageId.FolderOpen;
            }
            */

            if (!mUserPreference.DisplayNumberOfThumbnailImagesInFolderTree) {
                return item;
            }

#if true
            return item;
#else
            int nImage = thumbnailCache.NumberOfImageFilesInFolder(FullPath(node));
            string text;
            if (0 < nImage) {
                text = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} ({1})", node.Name, nImage);
            } else {
                text = node.Name;
            }
            if (!text.Equals(node.Text)) {
                node.Text = text;
            }
#endif
        }

        void OnTreeViewItemExpanded(object sender, System.Windows.RoutedEventArgs e) {
            var item = e.Source as TreeViewItem;

            // usb drive shows/disappears when user attached/detached
            TreeViewUpdateDriveNodes();

            TreeViewNodeSubTreesUpdate(item, true);
        }

        void OnTreeViewItemSelected(object sender, System.Windows.RoutedEventArgs e) {
            var item = e.Source as TreeViewItem;
            mSelectedDirectoryInfo = FolderPathToDirectoryInfo(FullPath(item));
            mListener.OnFolderTreeViewSelected(mSelectedDirectoryInfo);
            e.Handled = true;
        }

        /// <summary>
        /// update item text to show the number of images
        /// </summary>
        private void UpdateTreeViewItem(TreeViewItem item) {
        }
    }
}
